var querystring = require("querystring");
var url = require("url");
var settings = require("./settings");

var uuid = require('node-uuid');




//trims whitespaces from beginning and end
function trim(str) {
    var trimmedStr = str.replace(/^(\s*)((\S+\s*?)*)(\s*)$/, "$2");
    return trimmedStr;
}


myRequest = {};



function getResponseObj(socket) {
    var response = {


        uid: undefined,
        socket: socket,
        httpVersion: 'HTTP/1.1',
        status: 200,
        reasonPhrase: {
            "200": "OK",
            "400": "Bad Request",
            "405": "Method Not Allowed",
            "408": "Request Timeout",
            "404": "Not Found",
            "500": "Internal Server Error",
            "403": "Forbidden",
            "307": "Temporary Redirect"

        },
        headers: { 'Content-Type': 'text/html',
            'Connection': 'Keep-Alive'
        },

        body: "",

        timeoutID: 0,
        timerIsOn: false,
        setTimeOutTimer: function (response) {


            this.timeoutID = setTimeout(function () {


                response.status = 408;
                response.end();


                // }, 1000);//settings.TIMEOUT_MIN*60*1000);
            }, settings.TIMEOUT_MIN * 60 * 1000);

            response.timerIsOn = true;
        },

        end: function () {



            if (this.timerIsOn) {

                clearTimeout(this.timeoutID);
                response.timerIsOn = false;

            }

            if (this.forwardTo !== undefined) {

                this.doForward();
                return;

            } else if (this.redirectTo !== undefined) {

                var url = this.redirectTo["url"];
                this.status = 307;
                this.headers['Location'] = url;
            }

            if (this.status !== 200) {


                this.headers['Content-Type'] = 'text/html';

                var body = '<html>' +
                 '<head>' +
                 '<meta http-equiv="Content-Type" ' +
                 'content="text/html; charset=UTF-8" />' +
                  '<title>Error</title>' +
                  '</head>' +
                  '<body>' +
                  '<h1>' + this.status + " " + this.reasonPhrase[this.status] + '</h1>';
                if (this.status === 307) {
                    body += '<p>This page has moved to <a href="' + this.redirectTo["url"] + '">'
                    + this.redirectTo["url"] + '</a>.</p>'
                }

                body += '</body>' +
                  '</html>';
                this.body = body;

                this.headers['Content-Length'] = this.body.length;

            }


            var heads = this.prepareResponseHeaders();

            try {
                socket.write(heads + this.body);
            } catch (err) {

               // console.log("err in socket.write() from end():" + err.message);
            }
            //check keep alive status
            if ((this.httpVersion === 'HTTP/1.0' && this.headers['Connection'].toLowerCase() !== 'keep-alive') ||
				this.headers['Connection'].toLowerCase() === 'close') {
                //console.log("closing socket!!!!");
                socket.end();
            }
        },


        prepareResponseHeaders: function () {


            var headersValue = "";
            for (var key in this.headers) {

                headersValue += key + ":" + this.headers[key].toString() + "\r\n";

            }



            var date = new Date();
            var line1 = this.httpVersion + " " + this.status + " " + this.reasonPhrase[this.status] + " \r\n";
            var line2 = "Date:" + date.toString() + "\r\n";


            if (this.headers["Content-Length"] === undefined) {

                var line4 = "Content-Length:" + this.body.length + " \r\n";

            } else {

                var line4 = "";

            }


            for (var l = 0; l < this.cookies.length; l++) {

                headersValue += "set-cookie: " + this.cookies[l].key + "=" + this.cookies[l].value;
                if (this.cookies[l].domain !== undefined) {
                    headersValue += "; Domain=" + this.cookies[l].domain;
                }

                headersValue += "; Path=" + this.cookies[l].path;

                if (this.cookies[l].expire !== undefined) {
                    headersValue += "; Expires=" + this.cookies[l].expire.toUTCString();

                }

                if (this.cookies[l].httpOnly !== false) {
                    headersValue += "; HttpOnly";
                }

                headersValue += "\r\n";


            }

            var line3 = headersValue;


            var line5 = "\r\n";

            return line1 + line2 + line3 + line4 + line5;

        },


        write: function () {

            var heads = this.prepareResponseHeaders();
            try {
                socket.write(heads);
            } catch (err) {
                throw err;
            }
        },




        cookies: new Array(),

        addCookie: function (cookie) {
            this.cookies.push(cookie);
        },
        deleteCookie: function (key) {

            var index = -1;
            var i = 0;
            while (i < cookies.length && index == -1) {

                if (this.cookies[i].key == key) {
                    index = i;

                }
                ++i;
            }
            this.cookies.splice(index, 1);
        },




        createCookies: function (key, value, domain, path, expire, httpOnly) {

            var cookie = {
                key: trim(key),
                value: trim(value),
                path: path,
                domain: domain,
                expire: expire,
                httpOnly: httpOnly
            };
            return cookie;

        }

        ,

        forwardTo: undefined
        ,
        forward: function (resource, request, response) {

            this.forwardTo = { "resource": resource, "request": request, "response": response };

        },
        doForward: undefined,
        redirectTo: undefined
        ,
        redirect: function (url) {
            this.redirectTo = { "url": url };

        }


    };

  

    return response;
}



function getRequestObj(request, allSessions, apContext, sessionExperation) {


    var str = request;

    //1. first check that the request is of HTTP format
    try {
        var lines = str.split("\r\n");

        //get the 3 parts of the first line : [GET/POST] [Resource] [HTTP]
        var componets = lines[0].split(" ");
        var pattern = /HTTP\/1.(0|1)/;

        var st = trim(componets[2]);

        if (!pattern.test(st)) {

            throw "Bad HTTP format";

        }

     
        myRequest.httpVersion = st;

        myRequest.headers = {};
        
        //1.0 default should be close
        if (st === 'HTTP/1.0') {
            myRequest.headers['Connection'] = 'close';
        } else {
            myRequest.headers['Connection'] = 'keep-alive';
              
        }

    } catch (err) {
        //return status code 400
        throw 400;

    }




    //2. check traversal attack
    var traversalAttack = str.indexOf("../");
    if (traversalAttack !== -1) {

        throw 400;

    }

    myRequest.cookies = new Array();
    myRequest.getCookies = function () {

        return this.cookies;
    };

    //3. GET/POST Request
    var strMethod = trim(componets[0]);

    var urlStr = url.parse(componets[1]);

    if (strMethod === "GET") {

        myRequest.method = "GET";

        myRequest.resource = urlStr.pathname;
        myRequest.parameters = null;
        if (urlStr.search !== undefined) {
            var params = urlStr.search.slice(1);  //get rid of '?'
            myRequest.parameters = querystring.parse(params);
        }

     
       

        getHeaders(str);

    } else if (strMethod === "POST") {

        myRequest.method = "POST";

        myRequest.resource = urlStr.pathname;

        getHeaders(str);

    } else {

        throw 405;
    }

    var tempUid = -1;
    for (var z = 0; z < myRequest.cookies.length;z++ ) {

        if (myRequest.cookies[z].key === settings.SESSION_COOKIE_KEY) {
            tempUid = myRequest.cookies[z].value;
            break;
        }
    }




    if ((tempUid == -1) || (typeof allSessions[tempUid] === 'undefined')) {
        myRequest.uid = uuid.v4();
        allSessions[myRequest.uid] = {};

      

    } else {


        myRequest.uid = tempUid;
        var expDate = new Date( sessionExperation[myRequest.uid]);
        var texp = expDate.getTime();
        var now=new Date().getTime();

       
        if (texp <now ) {
            //you have expired, here is a new session
            myRequest.uid = uuid.v4();
            allSessions[myRequest.uid] = {};

        } 

      
        
        }
    //set new timeout
    var d1 = new Date();
    var d2 = new Date(d1);
    d2.setMinutes(d1.getMinutes() + settings.SESSION_TIMEOUT_MIN);
   // d2.setMilliseconds(d1.getMilliseconds() + 1000); //settings.SESSION_TIMEOUT_MIN);

    sessionExperation[myRequest.uid] = d2;
    

    

    myRequest.logOutSession = function () {
        
        if(typeof allSessions[myRequest.uid] !== 'undefined')
        {
            allSessions[myRequest.uid]={};
            
        }
        if(typeof sessionExperation[myRequest.uid] !== 'undefined')
        {
            sessionExperation[myRequest.uid]={};
        }
    };



    myRequest.getSession = function () {

        return allSessions[this.uid];
    };

    myRequest.getSessionUUID = function () {
        return this.uid;
    };

    myRequest.getApplicationContext = function () {

        return apContext;
    };




    //ex5 adittion
    myRequest.attributes = {};

    myRequest.setAttribute=function(key,val){
    
       this.attributes[key] = val;
    };


    myRequest.getAttribute = function (key) {

        return this.attributes[key];
    };

    myRequest.require = function (moduleName) {

        var module = require(moduleName);
        return module;
    }

    return myRequest;
}

function getHeaders(str) {
   var lines = str.split("\r\n");
    myRequest.body = "";
    
    for (var i = 1; i < lines.length; ++i) {

        //there is one empty line before the body
        if (lines[i].length == 0) {
            ++i;

            if (i < lines.length && lines[i].length != 0) {

                myRequest.body = lines[i];
                myRequest.parameters = querystring.parse(lines[i]);


            }

            continue;
        }

       var head = querystring.parse(lines[i], sep = '\r\n', eq = ':');
        for (var key in head) {

           
            var value=trim(head[key]);
            key = trim(key);

            //find the cookies
            if (key === 'Cookie') {
                var values = querystring.parse(value, sep = ';', eq = '=');

                for (var cookie in values) {
                    varCookieKey = trim(cookie);
                    
                    var cookie = {
                        key: varCookieKey,
                        value: trim(values[cookie]),
                        path: "/",
                        domain : undefined,
                        expire: undefined,
                        httpOnly: false
                    };

                    myRequest.cookies.push(cookie);

                }

            }
            
            myRequest.headers[key] = value;

        }

    }
}

exports.getRequestObj = getRequestObj;
exports.getResponseObj = getResponseObj;
